#import "BCSinceUptime.h"
#import <sys/sysctl.h>

@interface BCSinceUptime ()

@property (nonatomic, strong) NSHashTable    *bindedDelegates;
@property (nonatomic, assign) NSTimeInterval resignSystemUpTime;
@property (nonatomic, assign) NSTimeInterval becomeSystemUpTime;

@end

@implementation BCSinceUptime

static BCSinceUptime *kTimeInstance;
+ (instancetype)shared {
    static dispatch_once_t kOnceToken;
    dispatch_once(&kOnceToken, ^{
        kTimeInstance = [[[self class] alloc] init];
    });
    return kTimeInstance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _bindedDelegates = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory capacity:0];
        
        [self start];
    }
    return self;
}
///MARK: - public
- (void)start {
    [self end];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];
}
- (void)end {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    self.time = 0;
}

- (void)bind:(id<BCSinceUptimeDelegate>)delegate {
    if (!delegate ) {
        return;
    }
    if(![_bindedDelegates containsObject:delegate]){
        [_bindedDelegates addObject:delegate];
    }
}

- (void)unbind:(id<BCSinceUptimeDelegate>)delegate {
    [_bindedDelegates removeObject:delegate];
}
///MARK: - notification
- (void)applicationWillResignActive:(NSNotification *)notification {
    self.resignSystemUpTime = [BCSinceUptime uptimeSinceLastBoot];
    
    // 保证实体安全
    NSHashTable *bindDelegates  = [self.bindedDelegates mutableCopy];
    for (id<BCSinceUptimeDelegate> bindItem in bindDelegates) {
        if([bindItem respondsToSelector:@selector(uptimeWillResignActive:notification:)]){
            [bindItem uptimeWillResignActive:self notification:notification];
        }
    }
  
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
    if (self.resignSystemUpTime < 1) {
        return;
    }
    self.becomeSystemUpTime = [BCSinceUptime uptimeSinceLastBoot];
    self.time = floor(self.becomeSystemUpTime - self.resignSystemUpTime);
    
    // 保证实体安全
    NSHashTable *bindDelegates  = [self.bindedDelegates mutableCopy];
    for (id<BCSinceUptimeDelegate> bindItem in bindDelegates) {
        if([bindItem respondsToSelector:@selector(uptimeDidBecomeActive:notification:)]){
            [bindItem uptimeDidBecomeActive:self notification:notification];
        }
    }
}

///系统当前运行了多长时间
///因为两个参数都会受用户修改时间的影响，因此它们想减的值是不变的
+ (NSTimeInterval)uptimeSinceLastBoot {
    //获取当前设备时间时间戳 受用户修改时间影响
    struct timeval now;
    struct timezone tz;
    gettimeofday(&now, &tz);
    
    //获取系统上次重启的时间戳 受用户修改时间影响
    struct timeval boottime;
    int mib[2] = {CTL_KERN, KERN_BOOTTIME};
    size_t size = sizeof(boottime);
    
    double uptime = -1;
    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) {
        //获取上次启动时间成功
        //秒
        uptime = now.tv_sec - boottime.tv_sec;
        //微秒
        uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0;
    }
    return uptime;
}

@end
